#include "ssd_dftl.h"

void DFTL_init_arr();
void DFTL_Hit_Ghost_CMT(int blkno);
void DFTL_Hit_Real_CMT(int blkno);
void DFTL_Real_Cmt_Full();
void DFTL_Ghost_Cmt_Full();

void DFTL_Scheme(unsigned int *pageno,int *req_size,int operation)
{
    int blkno=(*pageno),cnt=(*req_size);
    int pos=-1,pos_real=-1,pos_ghost=-1;
    int min_ghost=-1;

    //变量的初始化
    if (init_flag==0)
    {
        MAP_REAL_MAX_ENTRIES = 3276;  // real map table size in bytes
        MAP_GHOST_MAX_ENTRIES = 820;
        real_arr=(int *)malloc(sizeof(int)*MAP_REAL_MAX_ENTRIES);
        ghost_arr=(int *)malloc(sizeof(int)*MAP_GHOST_MAX_ENTRIES);
        DFTL_init_arr();
        init_flag=1;
    }
    //1. pagemap in SRAM 
    rqst_cnt++;
    if((opagemap[blkno].map_status == MAP_REAL) || (opagemap[blkno].map_status == MAP_GHOST))
    {
        cache_hit++;
        opagemap[blkno].map_age++;
        if(opagemap[blkno].map_status == MAP_GHOST)
        {
            DFTL_Hit_Ghost_CMT(blkno);
            cache_slcmt_hit++;
        }
        else if(opagemap[blkno].map_status == MAP_REAL) 
        {
            DFTL_Hit_Real_CMT(blkno);
            cache_scmt_hit++;
        }
        else {
            printf("forbidden/shouldnt happen real =%d , ghost =%d\n",MAP_REAL,MAP_GHOST);
        }
    }

    //2. opagemap not in SRAM 
    else
    {
        //if map table in SRAM is full
        DFTL_Real_Cmt_Full();

        //read entry into real
        flash_hit++;
        // read from 2nd mapping table
        send_flash_request(((blkno-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE)*4, 4, 1, 2); 
        opagemap[blkno].map_status = MAP_REAL;
        opagemap[blkno].map_age = opagemap[real_max].map_age + 1;
        real_max = blkno;
        MAP_REAL_NUM_ENTRIES++;
        
        pos = find_free_pos(real_arr,MAP_REAL_MAX_ENTRIES);
        real_arr[pos] = blkno;
    }

    //comment out the next line when using cache
    if(operation==0){
        write_count++;
        opagemap[blkno].update = 1;
    }
    else
        read_count++;

    send_flash_request(blkno*4, 4, operation, 1); 
    blkno++;
    (*pageno)=blkno;
    (*req_size)=cnt;
}

void DFTL_init_arr()
{
    int i;
    for( i= 0; i< MAP_GHOST_MAX_ENTRIES;i++){
        ghost_arr[i] = -1;
    }

    for( i = 0; i < MAP_REAL_MAX_ENTRIES; i++) {
        real_arr[i] = -1;
    }
    MAP_REAL_NUM_ENTRIES=0;
    MAP_GHOST_NUM_ENTRIES=0;
}

void DFTL_Hit_Ghost_CMT(int blkno)
{
    int pos_ghost=-1,pos_real=-1;

    if ( real_min == -1 ) {
        real_min = 0;
        find_real_min();
    }    
    if(opagemap[real_min].map_age <= opagemap[blkno].map_age) 
    {
        find_real_min();  // probably the blkno is the new real_min alwaz
        opagemap[blkno].map_status = MAP_REAL;
        opagemap[real_min].map_status = MAP_GHOST;

        pos_ghost = search_table(ghost_arr,MAP_GHOST_MAX_ENTRIES,blkno);
        ghost_arr[pos_ghost] = -1;
        
        pos_real = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
        real_arr[pos_real] = -1;

        real_arr[pos_real]  = blkno; 
        ghost_arr[pos_ghost] = real_min; 
    }
}

void DFTL_Hit_Real_CMT(int blkno)
{
    if ( real_max == -1 ) {
        real_max = 0;
        find_real_max();
        printf("Never happend\n");
    }

    if(opagemap[real_max].map_age <= opagemap[blkno].map_age){
        real_max = blkno;
    }  
}

void DFTL_Real_Cmt_Full()
{
    int pos=-1;
    if((MAP_REAL_MAX_ENTRIES - MAP_REAL_NUM_ENTRIES) == 0)
    {
        DFTL_Ghost_Cmt_Full();

        //evict one entry from real cache to ghost cache 
        MAP_REAL_NUM_ENTRIES--;
        find_real_min();
        opagemap[real_min].map_status = MAP_GHOST;
        pos = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
        real_arr[pos]=-1;
        pos = find_free_pos(ghost_arr, MAP_GHOST_MAX_ENTRIES);
        ghost_arr[pos] = real_min;
        MAP_GHOST_NUM_ENTRIES++;
    }
}


void DFTL_Ghost_Cmt_Full()
{
    int ghost_min=-1, pos=-1;

    //evict one entry from ghost cache to DRAM or Disk, delay = DRAM or disk write, 1 oob write for invalidation 
    if((MAP_GHOST_MAX_ENTRIES - MAP_GHOST_NUM_ENTRIES) == 0)
    { 
        ghost_min = find_min_ghost_entry();
        evict++;//ymb

        if(opagemap[ghost_min].update == 1) {
            update_reqd++;//ymb
            opagemap[ghost_min].update = 0;
            // read from 2nd mapping table then update it
            send_flash_request(((ghost_min-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE)*4, 4, 1, 2);  
            // write into 2nd mapping table 
            send_flash_request(((ghost_min-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE)*4, 4, 0, 2);   
        } 
        opagemap[ghost_min].map_status = MAP_INVALID;
        opagemap[ghost_min].map_age = 0;
        MAP_GHOST_NUM_ENTRIES--;

        pos = search_table(ghost_arr,MAP_GHOST_MAX_ENTRIES,ghost_min);
        ghost_arr[pos]=-1;
    }
}